目前lib.rs
的內容如下
//// lib.rs
mod handler;
use handler::*;
use axum::Router;
use axum::routing::get;
use serde::Deserialize;
pub async fn run(app: Router) {
axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
.serve(app.into_make_service())
.await
.unwrap();
}
pub fn build() -> Router {
let app = Router::new().route("/", get(health_check));
app
}
我想要進一步把這兩個方法移出lib.rs
首先我們建立一個application.rs
的檔案,並把這兩個方法移入:
//// application.rs
use axum::Router;
use axum::routing::get;
use crate::handler::health_check;
pub struct Application {
port: u16,
router: Router,
}
pub async fn run(app: Router) {
axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
.serve(app.into_make_service())
.await
.unwrap();
}
pub fn build() -> Router {
let app = Router::new().route("/", get(health_check));
app
}
這邊的意圖是將應用程式封裝提供一個一致的界面供進入點使用,應用程式本體會紀錄要監聽的port以及路由設定,我們可以將build方法視為準備整個應用程式,而run則是運行,所以將以上程式碼再改一下:
//// application.rs
use axum::Router;
use axum::routing::get;
use crate::handler::health_check;
pub struct Application {
port: u16,
router: Router,
}
impl Application {
pub async fn build() -> Self {
let router = Router::new()
.route("/", get(health_check));
Ok(Self {
port: 3000,
router,
})
}
pub async fn run(self) {
let addr = std::net::SocketAddr::from(([0, 0, 0, 0], self.port));
Server::bind(&addr)
.serve(self.router.into_make_service())
.await
.unwrap();
}
}
rust的一個特色是將資料與應用程式的方法分開來,與C#以類別將資料與方法封裝十分不同。經過上面的重構後,就可以將main.rs
調整成以下:
use zero_to_production::*;
#[tokio::main]
async fn main() {
Application::build().run().await;
}
由使用方來看,這樣的封裝意圖就變得很明顯,先是準備應用程式,並且運行應用程式。這時候再回來看lib.rs
:
mod handler;
mod application;
pub use application::*;
這就是昨天提到的組織模組,我們需要將handler
與application
模組加入整個專案套件中,值得注意的是這邊只有公開了application模組,因為對外部而言並不需要知道整個應用程式的運作。
目前的專案結構如下:
.
├── Cargo.lock
├── Cargo.toml
└── src
├── application.rs
├── handler
│ ├── health_check.rs
│ ├── mod.rs
│ └── subscribe.rs
├── lib.rs
└── main.rs
這個階段的重構到這裡大致完成,可以運行一下測試來確認一切正常,明天要繼續推進這隻API的功能。